package manager

import (
	log "ac-common-go/glog"
	"ac-julink/cache"
	"ac-julink/constants"
	"ac-julink/tool/config"
	"ac-julink/tool/data"
	"errors"
	"strings"
)

const (
	REST_SCHEMA = "http://"
	SLASH       = "/"

	WAREHOUSE_WAREHOUSE = "ac-julink-warehouse"
	PS                  = "ac-julink-ps"
)

var (
	WAREHOUSE_ERROR = errors.New("warehouse error occured")
	PS_ERROR        = errors.New("ps error occured")
)

type AcRestClient struct {
	fastHttpClient   *FastReqClient
	warehouseBaseUrl string
	psBaseUrl        string
}

func NewAcRestClient() *AcRestClient {
	hcTimeout, _ := config.GetConfigIntValue("HTTP", "timeout")
	dailTimeout, _ := config.GetConfigIntValue("HTTP", "dailTimeout")
	maxConnsPerHost, _ := config.GetConfigIntValue("HTTP", "maxConnsPerHost")
	maxConnectionWaitTimeoutSeconds, _ := config.GetConfigIntValue("HTTP", "maxConnectionWaitTimeoutSeconds")
	maxIdleConnDuration, _ := config.GetConfigIntValue("HTTP", "maxIdleConnDuration")
	maxIdemponentCallAttempts, _ := config.GetConfigIntValue("HTTP", "maxIdemponentCallAttempts")

	warehouseUrl := config.GetConfigValue("REMOTE_PEER", "warehouseUrl")
	psUrl := config.GetConfigValue("REMOTE_PEER", "psUrl")

	fastHttpClient := NewFastReqClient(hcTimeout, dailTimeout, maxConnsPerHost, maxConnectionWaitTimeoutSeconds, maxIdleConnDuration, maxIdemponentCallAttempts)

	log.Errorf("init fasthttpclient ConnPoolStrategy: %v", fastHttpClient.reqClient.ConnPoolStrategy)
	log.Errorf("init fasthttpclient ReadTimeout: %v", fastHttpClient.reqClient.ReadTimeout)
	log.Errorf("init fasthttpclient WriteTimeout: %v", fastHttpClient.reqClient.WriteTimeout)
	log.Errorf("init fasthttpclient MaxIdleConnDuration: %v", fastHttpClient.reqClient.MaxIdleConnDuration)
	log.Errorf("init fasthttpclient MaxConnWaitTimeout: %v", fastHttpClient.reqClient.MaxConnWaitTimeout)
	log.Errorf("init fasthttpclient MaxConnDuration: %v", fastHttpClient.reqClient.MaxConnDuration)
	log.Errorf("init fasthttpclient MaxConnsPerHost: %v", fastHttpClient.reqClient.MaxConnsPerHost)

	return &AcRestClient{
		fastHttpClient:   fastHttpClient,
		warehouseBaseUrl: REST_SCHEMA + warehouseUrl,
		psBaseUrl:        REST_SCHEMA + psUrl,
	}
}

func (this *AcRestClient) WifiOnline(sstats data.WGPSstats) error {
	paramsMap := make(map[string]interface{})
	var resp ACBaseResponse

	paramsMap["wifiid"] = sstats.Wifiid
	paramsMap["owner"] = sstats.OwnerServer
	paramsMap["peeraddr"] = sstats.PeerAddr
	paramsMap["wifiver"] = sstats.WifiVersion
	paramsMap["stat"] = sstats.Stat
	paramsMap["netid"] = sstats.NetId
	paramsMap["protocol"] = sstats.Protocol
	paramsMap["deviceVer"] = sstats.DevVer

	if sstats.ProtVer == "HENC" {
		paramsMap["protVer"] = sstats.ProtVer
	}

	obj := make(map[string]interface{})
	obj["wgpsStatus"] = paramsMap
	obj["wifiid"] = sstats.Wifiid

	if err := this.fastHttpClient.FastPostDoWithReturnObj(this.composeUrl(this.psBaseUrl, PS, "wifiOnline"), obj, &resp); err != nil || resp.Code != REST_SUCCESS {
		if err != nil {
			log.Errorf("HTTP WifiOnline error for wifiId %s! %+v", sstats.Wifiid, err)
			return err
		}
		log.Errorf("HTTP WifiOnline bad result for wifiId %s! %s", sstats.Wifiid, resp.ErrorMsg)
		return errors.New(resp.ErrorMsg)
	} else {
		if resp.Code != REST_SUCCESS {
			return errors.New("Failed to operate wifiOnline for wifiId " + sstats.Wifiid)
		} else {
			return nil
		}
	}
}

func (this *AcRestClient) composeUrl(url string, subPaths ...string) string {
	return strings.Join(append([]string{url}, subPaths...), SLASH)
}

func (this *AcRestClient) GetWifiStats(wifiid string) (map[string]interface{}, error) {
	paramsMap := make(map[string]interface{})
	paramsMap["wifiid"] = wifiid
	var resp ACBaseResponse
	if err := this.fastHttpClient.FastPostDoWithReturnObj(this.composeUrl(this.psBaseUrl, PS, "getWifiStats"), paramsMap, &resp); err != nil || resp.Code != REST_SUCCESS {
		if err != nil {
			log.Errorf("HTTP GetWifiStats error for wifiId %s! %+v", wifiid, err)
			return nil, err
		}
		log.Errorf("HTTP GetWifiStats bad result for wifiId %s! %s", wifiid, resp.ErrorMsg)
		return nil, errors.New(resp.ErrorMsg)
	} else {
		if resp.Data != nil {
			return resp.MapToMap(), nil
		} else {
			return nil, PS_ERROR
		}
	}
}

func (this *AcRestClient) GetPublicKey(wifiid string, isHENC bool, context *cache.SyncContext) (string, error) {
	productInfo := context.Get(wifiid)
	if productInfo != nil {
		pre_key := productInfo.Prekey
		if len(pre_key) > 0 {
			return pre_key, nil
		}
	}
	paramsMap := make(map[string]interface{})
	paramsMap["physicalDeviceId"] = wifiid
	paramsMap["hencDevice"] = isHENC

	var resp ACBaseResponse
	url := this.composeUrl(this.warehouseBaseUrl, WAREHOUSE_WAREHOUSE, "getPublicKey")
	if err := this.fastHttpClient.FastPostDoWithReturnObj(url, paramsMap, &resp); err != nil || resp.Code != REST_SUCCESS {
		if err != nil {
			log.Errorf("HTTP GetPublicKey error for wifiId %s! %+v", wifiid, err)
			return "", err
		}
		log.Errorf("HTTP GetPublicKey bad result for wifiId %s! %s", wifiid, resp.ErrorMsg)
		return "", errors.New(resp.ErrorMsg)
	} else {
		preKey := resp.MapDataToString()
		if len(preKey) > 0 {
			context.Set(wifiid, "", "", preKey)
			return preKey, nil
		} else {
			return "", WAREHOUSE_ERROR
		}
	}
}

func (this *AcRestClient) GetCipheredDKey(wifi_id string, cryptType int, update bool, version int) (string, map[string]interface{}, error) {
	paramsMap := make(map[string]interface{})
	paramsMap["wifiId"] = wifi_id
	if cryptType == constants.CRYPT_AES_256_CBC {
		paramsMap["cbcEnable"] = true
	} else {
		paramsMap["cbcEnable"] = false
	}
	paramsMap["update"] = update
	paramsMap["version"] = version

	var resp ACBaseResponse
	if err := this.fastHttpClient.FastPostDoWithReturnObj(this.composeUrl(this.warehouseBaseUrl, WAREHOUSE_WAREHOUSE, "getCipheredDKey"), paramsMap, &resp); err != nil || resp.Code != REST_SUCCESS {
		if err != nil {
			log.Errorf("HTTP getCipheredDKey error for wifiId %s! %+v", wifi_id, err)
			return "", nil, err
		}
		log.Errorf("HTTP getCipheredDKey bad result for wifiId %s! %s", wifi_id, resp.ErrorMsg)
		return "", nil, errors.New(resp.ErrorMsg)
	} else {
		respMap := resp.MapToMap()
		if nil != respMap {
			return respMap["Dkey"].(string), respMap["fetchLatestCertInnerResp"].(map[string]interface{}), nil
		} else {
			return "", nil, WAREHOUSE_ERROR
		}
	}
}

func (this *AcRestClient) CipherWithDKey(wifi_id, payload string, cryptType int) (string, error) {
	paramsMap := make(map[string]interface{})
	paramsMap["wifiId"] = wifi_id
	if cryptType == constants.CRYPT_AES_256_CBC {
		paramsMap["cbcEnable"] = true
	} else {
		paramsMap["cbcEnable"] = false
	}
	paramsMap["payload"] = payload
	var resp ACBaseResponse
	if err := this.fastHttpClient.FastPostDoWithReturnObj(this.composeUrl(this.warehouseBaseUrl, WAREHOUSE_WAREHOUSE, "cipherWithDKey"), paramsMap, &resp); err != nil || resp.Code != REST_SUCCESS {
		if err != nil {
			log.Errorf("HTTP CipherWithDKey error for wifiId %s! %+v", wifi_id, err)
			return "", err
		}
		log.Errorf("HTTP CipherWithDKey bad result for wifiId %s! %s", wifi_id, resp.ErrorMsg)
		return "", errors.New(resp.ErrorMsg)
	} else {
		respMap := resp.MapToMap()
		if nil != respMap {
			return respMap["payload"].(string), nil
		} else {
			return "", WAREHOUSE_ERROR
		}
	}
}

func (this *AcRestClient) AuthCheckToken(token string) (string, int, error) {
	paramsMap := make(map[string]interface{})
	var resp ACBaseResponse
	paramsMap["token"] = token

	if err := this.fastHttpClient.FastPostDoWithReturnObj(this.composeUrl(this.psBaseUrl, PS, "verify"), paramsMap, &resp); err != nil || resp.Code != REST_SUCCESS {
		if err != nil {
			log.Errorf("HTTP AuthCheckToken error for token %s! %+v", token, err)
			return "", REST_FAILED, err
		}
		log.Errorf("HTTP AuthCheckToken bad result for token %s! %s", token, resp.ErrorMsg)
		return "", REST_FAILED, errors.New(resp.ErrorMsg)
	} else {
		respMap := resp.MapToMap()
		if nil != respMap {
			return respMap["wifi_id"].(string), REST_SUCCESS, nil
		} else {
			return "", REST_FAILED, PS_ERROR
		}
	}
}

func (this *AcRestClient) ActivateDevice(wifi_id string) (string, bool, error) {
	paramsMap := make(map[string]interface{})
	var resp ACBaseResponse
	paramsMap["physicalDeviceId"] = wifi_id
	paramsMap["needToken"] = true

	if err := this.fastHttpClient.FastPostDoWithReturnObj(this.composeUrl(this.warehouseBaseUrl, WAREHOUSE_WAREHOUSE, "activateDevice"), paramsMap, &resp); err != nil || resp.Code != REST_SUCCESS {
		log.Errorf("HTTP activateDevice error for wifiId %s! %+v", wifi_id, err)
		if err != nil {
			return "", false, err
		}
		log.Errorf("HTTP activateDevice bad result for wifiId %s! %s", wifi_id, resp.ErrorMsg)
		return "", false, errors.New(resp.ErrorMsg)
	} else {
		resultMap := resp.MapToMap()
		if nil != resultMap {
			token := resultMap["token"]
			activated := resultMap["activated"]

			return token.(string), activated.(bool), nil
		} else {
			return "", false, WAREHOUSE_ERROR
		}
	}
}
